defmodule EliPhxPlay.ChatSchema.GroupsTest do
  use EliPhxPlay.DataCase
  alias EliPhxPlay.ChatSchema.{Groups, Users}
  alias EliPhxPlay.Repo

  describe "changeset/2" do
    setup do
      # 修正：使用正确的 Users schema 字段
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "test_owner",
        password: "password123",
        nickname: "测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "创建群聊时有效的数据", %{owner: owner} do
      attrs = %{
        group_name: "测试群聊",
        group_description: "这是一个测试群聊",
        group_avatar: "avatar_123.jpg",
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)

      assert changeset.valid?
      assert changeset.changes.group_name == "测试群聊"
      assert changeset.changes.group_description == "这是一个测试群聊"
      assert changeset.changes.group_avatar == "avatar_123.jpg"
      assert changeset.changes.owner_id == owner.user_id
    end

    test "仅有必填字段也能创建群聊", %{owner: owner} do
      attrs = %{
        group_name: "最小群聊",
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)

      assert changeset.valid?
      assert changeset.changes.group_name == "最小群聊"
      assert changeset.changes.owner_id == owner.user_id
    end

    test "缺少群名时验证失败" do
      attrs = %{group_description: "没有群名的群聊"}

      changeset = Groups.changeset(%Groups{}, attrs)

      refute changeset.valid?
      assert %{group_name: ["can't be blank"]} = errors_on(changeset)
    end

    test "缺少群主ID时验证失败" do
      attrs = %{group_name: "没有群主的群聊"}

      changeset = Groups.changeset(%Groups{}, attrs)

      refute changeset.valid?
      assert %{owner_id: ["can't be blank", "新群主ID不能为空"]} = errors_on(changeset)
    end
  end

  describe "群名验证" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "test_owner",
        password: "password123",
        nickname: "测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "群名长度在有效范围内", %{owner: owner} do
      # 测试1字符群名
      attrs = %{group_name: "A", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?

      # 测试50字符群名
      long_name = String.duplicate("测", 50)
      attrs = %{group_name: long_name, owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "群名过长时验证失败", %{owner: owner} do
      long_name = String.duplicate("测", 51)
      attrs = %{group_name: long_name, owner_id: owner.user_id}

      changeset = Groups.changeset(%Groups{}, attrs)

      refute changeset.valid?
      assert %{group_name: ["群名长度必须在1-50字符之间"]} = errors_on(changeset)
    end

    test "群名不能以空格开头或结尾", %{owner: owner} do
      # 以空格开头
      attrs = %{group_name: " 测试群聊", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      refute changeset.valid?
      assert %{group_name: ["群名不能以空格开头或结尾"]} = errors_on(changeset)

      # 以空格结尾
      attrs = %{group_name: "测试群聊 ", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      refute changeset.valid?
      assert %{group_name: ["群名不能以空格开头或结尾"]} = errors_on(changeset)

      # 两端都有空格
      attrs = %{group_name: " 测试群聊 ", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      refute changeset.valid?
      assert %{group_name: ["群名不能以空格开头或结尾"]} = errors_on(changeset)
    end

    test "单字符群名不受空格规则影响", %{owner: owner} do
      attrs = %{group_name: "A", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end
  end

  describe "群描述验证" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "test_owner",
        password: "password123",
        nickname: "测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "群描述可以为空", %{owner: owner} do
      attrs = %{group_name: "测试群", group_description: "", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "群描述在有效长度内", %{owner: owner} do
      description = String.duplicate("测", 500)
      attrs = %{group_name: "测试群", group_description: description, owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "群描述过长时验证失败", %{owner: owner} do
      description = String.duplicate("测", 501)
      attrs = %{group_name: "测试群", group_description: description, owner_id: owner.user_id}

      changeset = Groups.changeset(%Groups{}, attrs)

      refute changeset.valid?
      assert %{group_description: ["群描述不能超过500字符"]} = errors_on(changeset)
    end
  end

  describe "头像路径验证" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "test_owner",
        password: "password123",
        nickname: "测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "有效的头像文件路径", %{owner: owner} do
      valid_paths = [
        "avatar_123.jpg",
        "group/avatar_456.png",
        "user_789.jpeg",
        "test-image.gif",
        "profile_pic.webp"
      ]

      for path <- valid_paths do
        attrs = %{group_name: "测试群", group_avatar: path, owner_id: owner.user_id}
        changeset = Groups.changeset(%Groups{}, attrs)
        assert changeset.valid?, "路径 #{path} 应该是有效的"
      end
    end

    test "头像路径可以为空", %{owner: owner} do
      attrs = %{group_name: "测试群", group_avatar: "", owner_id: owner.user_id}
      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "无效的头像文件路径", %{owner: owner} do
      invalid_paths = [
        "/absolute/path.jpg",  # 绝对路径
        "image.txt",          # 不支持的文件类型
        "image..jpg",         # 包含目录遍历
        "../image.jpg",       # 目录遍历攻击
        "image with space.jpg", # 包含空格
        "image@special.jpg"   # 特殊字符
      ]

      for path <- invalid_paths do
        attrs = %{group_name: "测试群", group_avatar: path, owner_id: owner.user_id}
        changeset = Groups.changeset(%Groups{}, attrs)
        refute changeset.valid?, "路径 #{path} 应该是无效的"
        assert %{group_avatar: ["头像文件路径格式不正确"]} = errors_on(changeset)
      end
    end
  end

  describe "update_changeset/2" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "test_owner",
        password: "password123",
        nickname: "测试群主"
      } |> Repo.insert!()

      group = %Groups{
        group_id: Ecto.UUID.generate(),
        group_name: "原始群名",
        group_description: "原始描述",
        owner_id: owner.user_id
      }

      {:ok, group: group, owner: owner}
    end

    test "更新群信息时有效的数据", %{group: group} do
      attrs = %{
        group_name: "新群名",
        group_description: "新描述",
        group_avatar: "new_avatar.jpg"
      }

      changeset = Groups.update_changeset(group, attrs)

      assert changeset.valid?
      assert changeset.changes.group_name == "新群名"
      assert changeset.changes.group_description == "新描述"
      assert changeset.changes.group_avatar == "new_avatar.jpg"
    end

    test "更新时群名仍然是必填的", %{group: group} do
      attrs = %{group_name: "", group_description: "新描述"}

      changeset = Groups.update_changeset(group, attrs)

      refute changeset.valid?
      assert %{group_name: ["can't be blank"]} = errors_on(changeset)
    end
  end

  describe "transfer_ownership_changeset/2" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "old_owner",
        password: "password123",
        nickname: "原群主"
      } |> Repo.insert!()

      new_owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "new_owner",
        password: "password123",
        nickname: "新群主"
      } |> Repo.insert!()

      group = %Groups{
        group_id: Ecto.UUID.generate(),
        group_name: "测试群",
        owner_id: owner.user_id
      }

      {:ok, group: group, new_owner: new_owner}
    end

    test "有效的群主转让", %{group: group, new_owner: new_owner} do
      attrs = %{owner_id: new_owner.user_id}

      changeset = Groups.transfer_ownership_changeset(group, attrs)

      assert changeset.valid?
      assert changeset.changes.owner_id == new_owner.user_id
    end

    test "转让时新群主ID不能为空", %{group: group} do
      attrs = %{owner_id: nil}

      changeset = Groups.transfer_ownership_changeset(group, attrs)

      refute changeset.valid?
      assert %{owner_id: ["can't be blank", "新群主ID不能为空"]} = errors_on(changeset)
    end

    test "转让时新群主ID格式不正确", %{group: group} do
      attrs = %{owner_id: ""}

      changeset = Groups.transfer_ownership_changeset(group, attrs)

      refute changeset.valid?
      assert %{owner_id: ["新群主ID格式不正确"]} = errors_on(changeset)
    end
  end

  describe "avatar_url/1" do
    test "没有头像时返回默认头像" do
      group_without_avatar = %Groups{group_avatar: nil}
      assert Groups.avatar_url(group_without_avatar) == "/images/default-group-avatar.png"

      group_with_empty_avatar = %Groups{group_avatar: ""}
      assert Groups.avatar_url(group_with_empty_avatar) == "/images/default-group-avatar.png"
    end

    test "有头像时返回完整URL" do
      group = %Groups{group_avatar: "avatar_123.jpg"}
      assert Groups.avatar_url(group) == "/uploads/group_avatars/avatar_123.jpg"
    end
  end

  describe "avatar_file_path/1" do
    test "没有头像时返回nil" do
      group_without_avatar = %Groups{group_avatar: nil}
      assert Groups.avatar_file_path(group_without_avatar) == nil

      group_with_empty_avatar = %Groups{group_avatar: ""}
      assert Groups.avatar_file_path(group_with_empty_avatar) == nil
    end

    test "有头像时返回完整文件路径" do
      group = %Groups{group_avatar: "avatar_123.jpg"}
      expected_path = Path.join([
        Application.get_env(:eli_phx_play, :uploads_path, "priv/static/uploads"),
        "group_avatars",
        "avatar_123.jpg"
      ])
      assert Groups.avatar_file_path(group) == expected_path
    end
  end

  describe "generate_avatar_filename/2" do
    test "生成唯一的头像文件名" do
      group_id = Ecto.UUID.generate()
      original_filename = "test_image.jpg"

      filename1 = Groups.generate_avatar_filename(group_id, original_filename)
      filename2 = Groups.generate_avatar_filename(group_id, original_filename)

      # 确保文件名包含群组ID和扩展名
      assert String.contains?(filename1, group_id)
      assert String.ends_with?(filename1, ".jpg")

      # 确保每次生成的文件名都不同（因为时间戳不同）
      assert filename1 != filename2
    end

    test "保持原始文件扩展名" do
      group_id = Ecto.UUID.generate()

      test_cases = [
        {"test.png", ".png"},
        {"image.jpeg", ".jpeg"},
        {"avatar.gif", ".gif"},
        {"no_extension", ""}
      ]

      for {original, expected_ext} <- test_cases do
        filename = Groups.generate_avatar_filename(group_id, original)
        assert String.ends_with?(filename, expected_ext)
      end
    end
  end

  describe "数据库集成测试" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "db_test_owner",
        password: "password123",
        nickname: "数据库测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "成功创建群聊记录", %{owner: owner} do
      attrs = %{
        group_name: "数据库测试群",
        group_description: "用于测试数据库操作的群聊",
        group_avatar: "test_avatar.jpg",
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?

      {:ok, group} = Repo.insert(changeset)

      assert group.group_name == "数据库测试群"
      assert group.group_description == "用于测试数据库操作的群聊"
      assert group.group_avatar == "test_avatar.jpg"
      assert group.owner_id == owner.user_id
      assert group.group_id != nil
      assert group.inserted_at != nil
      assert group.updated_at != nil
    end

    test "成功更新群聊记录", %{owner: owner} do
      # 先创建群聊
      {:ok, group} = %Groups{
        group_name: "原始群名",
        group_description: "原始描述",
        owner_id: owner.user_id
      } |> Repo.insert()

      # 更新群聊信息
      update_attrs = %{
        group_name: "更新后的群名",
        group_description: "更新后的描述",
        group_avatar: "updated_avatar.png"
      }

      changeset = Groups.update_changeset(group, update_attrs)
      assert changeset.valid?

      {:ok, updated_group} = Repo.update(changeset)

      assert updated_group.group_name == "更新后的群名"
      assert updated_group.group_description == "更新后的描述"
      assert updated_group.group_avatar == "updated_avatar.png"
      assert updated_group.owner_id == owner.user_id
      assert updated_group.updated_at != group.updated_at
    end

    test "成功转让群主", %{owner: owner} do
      # 创建新的用户作为新群主
      new_owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "new_group_owner",
        password: "password123",
        nickname: "新群主"
      } |> Repo.insert!()

      # 创建群聊
      {:ok, group} = %Groups{
        group_name: "转让测试群",
        owner_id: owner.user_id
      } |> Repo.insert()

      # 转让群主
      changeset = Groups.transfer_ownership_changeset(group, %{owner_id: new_owner.user_id})
      assert changeset.valid?

      {:ok, transferred_group} = Repo.update(changeset)

      assert transferred_group.owner_id == new_owner.user_id
      assert transferred_group.group_name == "转让测试群"
    end

    test "级联查询 - 查询群主信息", %{owner: owner} do
      # 创建群聊
      {:ok, group} = %Groups{
        group_name: "级联测试群",
        owner_id: owner.user_id
      } |> Repo.insert()

      # 查询群聊并预加载群主信息
      group_with_owner = Repo.get(Groups, group.group_id) |> Repo.preload(:owner)

      assert group_with_owner.owner.account == "db_test_owner"
      assert group_with_owner.owner.nickname == "数据库测试群主"
    end
  end

  describe "边界情况测试" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "boundary_test_owner",
        password: "password123",
        nickname: "边界测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "处理nil值", %{owner: owner} do
      attrs = %{
        group_name: "测试群",
        group_description: nil,
        group_avatar: nil,
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "处理空字符串", %{owner: owner} do
      attrs = %{
        group_name: "测试群",
        group_description: "",
        group_avatar: "",
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)
      assert changeset.valid?
    end

    test "群名为空字符串时验证失败", %{owner: owner} do
      attrs = %{
        group_name: "",
        owner_id: owner.user_id
      }

      changeset = Groups.changeset(%Groups{}, attrs)
      refute changeset.valid?
      assert %{group_name: ["can't be blank"]} = errors_on(changeset)
    end

    test "owner_id为无效UUID时处理", %{owner: _owner} do
      attrs = %{
        group_name: "测试群",
        owner_id: "invalid-uuid"
      }

      changeset = Groups.changeset(%Groups{}, attrs)
      refute changeset.valid?
      assert %{owner_id: ["新群主ID格式不正确"]} = errors_on(changeset)
    end
  end

  describe "性能测试" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "perf_test_owner",
        password: "password123",
        nickname: "性能测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "批量创建群聊验证性能", %{owner: owner} do
      # 测试批量验证changeset的性能
      groups_data = for i <- 1..100 do
        %{
          group_name: "测试群#{i}",
          group_description: "第#{i}个测试群",
          group_avatar: "avatar_#{i}.jpg",
          owner_id: owner.user_id
        }
      end

      start_time = System.monotonic_time()

      changesets = Enum.map(groups_data, fn attrs ->
        Groups.changeset(%Groups{}, attrs)
      end)

      end_time = System.monotonic_time()
      elapsed = System.convert_time_unit(end_time - start_time, :native, :millisecond)

      # 验证所有changeset都是有效的
      assert Enum.all?(changesets, & &1.valid?)

      # 性能断言 - 100个changeset应该在合理时间内完成（比如小于1秒）
      assert elapsed < 1000, "批量验证用时 #{elapsed}ms，可能存在性能问题"
    end
  end

  describe "辅助函数单元测试" do
    test "valid_avatar_path?/1 私有函数测试" do
      # 由于这是私有函数，我们通过公共接口间接测试
      # 测试通过changeset验证来间接测试valid_avatar_path?函数
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "helper_test_owner",
        password: "password123",
        nickname: "辅助函数测试群主"
      } |> Repo.insert!()

      # 测试各种边界情况
      test_cases = [
        # {path, should_be_valid, description}
        {"image.jpg", true, "基本jpg文件"},
        {"folder/image.png", true, "文件夹中的png文件"},
        {"image_123.jpeg", true, "带数字的jpeg文件"},
        {"test-file.gif", true, "带连字符的gif文件"},
        {"file.WEBP", true, "大写扩展名的webp文件"},
        {"/absolute/path.jpg", false, "绝对路径"},
        {"../parent.jpg", false, "父目录引用"},
        {"file with space.jpg", false, "包含空格"},
        {"file@symbol.jpg", false, "包含特殊符号"},
        {"file.txt", false, "不支持的文件类型"},
        {"file", false, "没有扩展名"},
        {"", true, "空字符串（允许，使用默认头像）"}
      ]

      for {path, should_be_valid, description} <- test_cases do
        attrs = %{
          group_name: "测试群",
          group_avatar: path,
          owner_id: owner.user_id
        }

        changeset = Groups.changeset(%Groups{}, attrs)

        if should_be_valid do
          assert changeset.valid?, "#{description}: 路径 '#{path}' 应该有效"
        else
          case path do
            "" -> assert changeset.valid?  # 空字符串是特殊情况，允许的
            _ ->
              refute changeset.valid?, "#{description}: 路径 '#{path}' 应该无效"
              assert changeset.errors[:group_avatar] != nil, "应该有group_avatar错误信息"
          end
        end
      end
    end
  end

  describe "关联关系测试" do
    setup do
      owner = %Users{
        user_id: Ecto.UUID.generate(),
        account: "relation_test_owner",
        password: "password123",
        nickname: "关联测试群主"
      } |> Repo.insert!()

      {:ok, owner: owner}
    end

    test "用户可以拥有多个群组", %{owner: owner} do
      # 创建多个群组
      {:ok, _group1} = %Groups{
        group_name: "群组1",
        owner_id: owner.user_id
      } |> Repo.insert()

      {:ok, _group2} = %Groups{
        group_name: "群组2",
        owner_id: owner.user_id
      } |> Repo.insert()

      # 查询用户拥有的群组
      user_with_groups = Repo.get(Users, owner.user_id) |> Repo.preload(:owned_groups)

      assert length(user_with_groups.owned_groups) == 2
      group_names = Enum.map(user_with_groups.owned_groups, & &1.group_name)
      assert "群组1" in group_names
      assert "群组2" in group_names
    end

    test "群组属于一个群主", %{owner: owner} do
      {:ok, group} = %Groups{
        group_name: "测试群组",
        owner_id: owner.user_id
      } |> Repo.insert()

      group_with_owner = Repo.get(Groups, group.group_id) |> Repo.preload(:owner)

      assert group_with_owner.owner.user_id == owner.user_id
      assert group_with_owner.owner.account == "relation_test_owner"
      assert group_with_owner.owner.nickname == "关联测试群主"
    end
  end

  # 移除自定义的 errors_on 函数，使用 DataCase 中的版本
end
